
import java.util.Arrays;

public class ImageMethods {

	public static int getRipPos(double[][] map) {
		System.out.print("Searching for RIP position ..");
		double[] maxima = new double[map[0].length];
		int[] ripXcoord = new int[map[0].length];

		for (int x = 0; x < map.length; x++) {
			for (int y = 0; y < map[x].length; y++) {
				if (map[x][y] > maxima[y]) {
					maxima[y] = map[x][y];
					ripXcoord[y] = x;
				}
			}
		}
		Arrays.sort(ripXcoord);
		int ripPos = ripXcoord[(int) (ripXcoord.length * 0.5)];

		System.out.println(".. found at " + ripPos);
		return (ripPos);
	}

	public static double[][] substractSimilaritie(double[][] map) {
		System.out.print("Copmpensating RIPtailing ..");
		double medianPercentage = 0.3; // 0.3
		double[] similaritieSpectrum = new double[map.length];

		int stoppRT = map[0].length;
		int startRT = 0;

		double[] Ycolumn = new double[stoppRT - startRT];
		int quantilePosition = (int) (Ycolumn.length * medianPercentage);
		System.out.println("quantilepostion: " + quantilePosition);

		// compute similaritie
		for (int x = 0; x < map.length; x++) {
			for (int y = startRT; y < stoppRT; y++) {
				Ycolumn[y - startRT] = map[x][y];
			}
			Arrays.sort(Ycolumn);
			similaritieSpectrum[x] = Ycolumn[quantilePosition];
			// System.out.println(similaritieSpectrum[x]);
		}

		// substract similaritie from all spectra
		for (int x = 0; x < map.length; x++) {
			for (int y = 0; y < map[0].length; y++) {
				map[x][y] = Math.max(0, map[x][y] - similaritieSpectrum[x]);
			}
		}
		System.out.println(".. done");
		return map;
	}

	public static double[][] filterMedian(double[][] map, int maskSize) {
		System.out.print("Median filter (masksize:" + maskSize + ") ..");
		int xDim = map.length, yDim = map[0].length, maskR = (maskSize - 1) / 2, medianPos = ((maskSize * maskSize + 1) / 2) - 1, eC;
		double[][] newArray = new double[xDim][yDim];
		double[] elements = new double[maskSize * maskSize];

		for (int x = 0; x < newArray.length; x++) {
			for (int y = 0; y < newArray[0].length; y++) {
				eC = 0;
				for (int mx = maskR * -1; mx <= maskR; mx++) {
					for (int my = maskR * -1; my <= maskR; my++) {
						try {
							elements[eC] = map[x + mx][y + my];
						} catch (IndexOutOfBoundsException e) {
							elements[eC] = 0;
						}
						eC++;
					}
				}
				Arrays.sort(elements);
				newArray[x][y] = elements[medianPos]; // median
//				// overwrite spectra 1-3
//				if (y <= 3) {
//					newArray[x][y] = map[x][y];
//				}

			}
		}
		System.out.println(".. done");
		return (newArray);
	}

	public static double[][] filterGauss(double[][] map) {
		System.out.print("Using Gaussian Filter ..");
		double newArray[][] = new double[map.length][map[0].length];
		double[][] mask = { { 1, 2, 1 }, { 2, 4, 2 }, { 1, 2, 1 } };
		int maskRadius = (mask.length - 1) / 2;
		int maskSum = 0;
		for (int i = 0; i < mask.length; i++) {
			for (int j = 0; j < mask[i].length; j++) {
				maskSum += mask[i][j];
			}
		}

		for (int x = maskRadius; x < newArray.length - maskRadius; x++) {
			for (int y = maskRadius; y < newArray[x].length - maskRadius; y++) {
				for (int i = -maskRadius; i <= maskRadius; i++) {
					for (int j = -maskRadius; j <= maskRadius; j++) {
						newArray[x][y] += (map[x + i][y + j] * mask[i + maskRadius][j + maskRadius]) / maskSum;
					}
				}
			}
		}
		System.out.println(".. done");
		return (newArray);
	}

	public static double[][] normArray(double[][] map) {
		System.out.print("Computing min/max intensities Vs/cm� ..");

		double min = 99999, max = -99999;
		double newArray[][] = new double[map.length][map[0].length];
		// get min/max values

		int borderSkip = 5;

		for (int i = 0 + borderSkip; i < map.length - borderSkip; i++) {
			for (int j = 0 + borderSkip; j < map[i].length - borderSkip; j++) {
				if (map[i][j] > max) {
					max = map[i][j];
				}

				if (map[i][j] < min) {
					min = map[i][j];
				}

			}
		}

		System.out.println(".. min = " + min + " max = " + max);

		System.out.print("Normalizing values between " + min + " and " + max + " to [0,1] ..");

		for (int i = 0; i < map.length; i++) {
			for (int j = 0; j < map[i].length; j++) {
				double fnorm = (map[i][j] - min) / (max - min);
				newArray[i][j] = fnorm;
			}

		}
		System.out.println(".. done");
		return (newArray);
	}

	public static double[][] baselineCorrection(double[][] map) {

		int rippos = getRipPos(map);

		int xstart = (int) (rippos * 0.1);
		int xstopp = (int) (rippos * 0.8);
		int ystart = 3;
		int ystopp = map[0].length - 6;
		int dim = (xstopp - xstart) * (ystopp - ystart);

		double[] blc = new double[dim];
		int i = 0;
		for (int x = xstart; x < xstopp; x++) {
			for (int y = ystart; y < ystopp; y++) {
				blc[i] = map[x][y];
				i++;

			}

		}
		Arrays.sort(blc);
		double blcMedian = blc[(int) (dim * 0.9)];

		System.out.println("Baseline = " + blcMedian);

		for (int x = 0; x < map.length; x++) {
			for (int y = 0; y < map[0].length; y++) {
				map[x][y] = map[x][y] - blcMedian;
			}

		}

		return map;
	}

	public static double[][] baselineCorrectionSpectraWise(double[][] map) {

		int rippos = getRipPos(map);

		int xstart = (int) (rippos * 0.1);
		int xstopp = (int) (rippos * 0.8);

		int dim = (xstopp - xstart);

		for (int y = 0; y < map[0].length; y++) {

			double[] blc = new double[dim];
			int i = 0;
			for (int x = xstart; x < xstopp; x++) {
				blc[i] = map[x][y];
				i++;

			}

			Arrays.sort(blc);
			double blcMedian = blc[(int) (dim * 0.5)];

			for (int x = 0; x < map.length; x++) {
				map[x][y] = map[x][y] - blcMedian;
			}

		}

		return map;
	}

	// load Peaklists to layer panel
	public static double[][] limitPrecision(double[][] map, double precision) {
		for (int i = 0; i < map.length; i++) {
			for (int j = 0; j < map[0].length; j++) {
				map[i][j] = Math.round(map[i][j] * precision) / precision;
			}

		}
		return map;
	}

	public static double[][] savitzkyGolayFilter(double[][] map) {
		System.out.print("Savitzky-Golay Filter ..");
		double[][] filtredMap = new double[map.length][map[0].length];
		for (int x = 0; x < map.length; x++) {
			for (int y = 0; y < map[x].length; y++) {
				try {
					filtredMap[x][y] = (map[x + -3][y] * -3 + map[x + -2][y] * 12 + map[x + -1][y] * 17 + map[x + +0][y] * 35
							+ map[x + +1][y] * 17 + map[x + +2][y] * 12 + map[x + +3][y] * -3) / 87;
				} catch (IndexOutOfBoundsException e) {
					filtredMap[x][y] = map[x][y];
				}
			}
		}
		System.out.println(".. done");
		return filtredMap;
	}

	public static double[][] defaultFilterPipeline(double[][] map, boolean ripCompensation, boolean medianFilter, boolean sgFilter,
			boolean gaussFilter) {

		try {
			map = ImageMethods.baselineCorrectionSpectraWise(map);
		} catch (Exception x) {
			System.out.println("Spectrawise baselinecorrection failed, try to correct global");
			try {
				map = ImageMethods.baselineCorrection(map);
			} catch (Exception x2) {
				System.out.println("!!! ERROR !!! global baselinecorrection failed !!!");
			}

		}
		map = normArray(map);

		if (ripCompensation)
			map = ImageMethods.substractSimilaritie(map);

		if (medianFilter)
			map = filterMedian(map, 5);
		if (sgFilter)
			map = savitzkyGolayFilter(map);
		if (gaussFilter)
			map = filterGauss(map);

		map = limitPrecision(map, 10000);
		return map;

	}
}
